/*
 * Copyright 2013 meichenhui
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.googlecode.zet.tool.pluginanalyzer;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.Manifest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.osgi.service.resolver.BundleSpecification;
import org.osgi.framework.Constants;

/**
 * 插件依赖解析器<br/>
 * 用来生成依赖关系报告{@link DependenciesReport}
 * 
 * @author meichenhui
 * 
 */
public class DependencyAnalyzer {

    public SimpleBundleDescription createBundleDescription(String jarPath) throws ManifestNotFoundException {
        Manifest manifest = Util.readJarManifest(jarPath);
        if (manifest == null){
            throw new ManifestNotFoundException(jarPath);
        }
        return createBundleDescription(manifest);
    }
    
    public SimpleBundleDescription createBundleDescription(Manifest manifest) throws ManifestNotFoundException {
        if (manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) == null){
            throw new ManifestNotFoundException(manifest.toString());
        }
        return new SimpleBundleDescription(manifest);
    }
    
    public List<SimpleBundleDescription> createBundlesDescription(String pluginsDirectoryPath, ExceptionHandler exceptionHandler) throws DirectoryNotFoundException{
        List<SimpleBundleDescription> descriptions = new ArrayList<SimpleBundleDescription>();
        File pluginsDirectory = new File(pluginsDirectoryPath);
        if (!pluginsDirectory.isDirectory()) {
            throw new DirectoryNotFoundException();
        }
        Collection<File> jarsAndManifests = FileUtils.listFiles(pluginsDirectory, new String[]{"jar","JAR","mf","MF"}, true);
        for (File aPluginJar : jarsAndManifests) {
            if (aPluginJar.getName().substring(aPluginJar.getName().lastIndexOf(".")).toUpperCase().equals(".JAR")){
                addJarManifest(exceptionHandler, descriptions, aPluginJar);
            } else {
                addManifest(exceptionHandler, descriptions, aPluginJar);
            }
        }
        return descriptions;
    }

    protected void addJarManifest(ExceptionHandler exceptionHandler,
            List<SimpleBundleDescription> descriptions, File aPluginJar) {
        try {
            descriptions.add(createBundleDescription(aPluginJar.getPath()));
        } catch (ManifestNotFoundException e) {
            if (exceptionHandler == null){
                return;
            }
            exceptionHandler.handle(e);
        }
    }
    
    protected void addManifest(ExceptionHandler exceptionHandler,
            List<SimpleBundleDescription> descriptions, File aPluginJar) {
        try {
            descriptions.add(createBundleDescription(new Manifest(new FileInputStream(aPluginJar))));
        } catch (Exception e) {
            if (exceptionHandler == null){
                return;
            }
            exceptionHandler.handle(e);
        }
    }
    
    public Set<BundleSpecification> computeMissingPlugins(
            SimpleBundleDescription[] registedPlugins,
            SimpleBundleDescription[] inputPlugins) {
        Set<SimpleBundleDescription> registedPluginsSet = new HashSet<SimpleBundleDescription>(
                Arrays.asList(registedPlugins == null ? new SimpleBundleDescription[0]
                        : registedPlugins));
        if (inputPlugins == null){
            return Collections.emptySet();
        }
        for (SimpleBundleDescription anInputPlugin : inputPlugins) {
            registedPluginsSet.add(anInputPlugin);
        }
        // 判断目标插件的依赖项是否在已注册的插件列表中
        // 收集不存在的插件
        Set<BundleSpecification> missingPlugins = new HashSet<BundleSpecification>();
        for (SimpleBundleDescription anInputPlugin : inputPlugins) {
            for (BundleSpecification aRequireBundle : anInputPlugin
                    .getRequiredBundles()) {
                boolean isSatisfied = false;
                for (SimpleBundleDescription aRegistedPlugin : registedPluginsSet) {
                    if (aRequireBundle.isSatisfiedBy(aRegistedPlugin)) {
                        isSatisfied = true;
                    }
                }
                if (!isSatisfied) {
                    missingPlugins.add(aRequireBundle);
                }
            }
        }
        return missingPlugins;
    }

}
